﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Serialization;

using Microsoft.WowAddonStudio.Lua.Support;

namespace Microsoft.WowAddonStudio.Lua.Model
{
    [XmlRoot("doc", Namespace = "http://schemas.microsoft.com/aos/2008/xmldoc")]
    public class Documentation : Table
    {
        /// <summary>
        /// Gets the functions declared in the documentation.
        /// </summary>
        public IEnumerable<Function> Functions
        {
            get { return this.Declarations != null ? this.Declarations.OfType<Function>() : new Function[0]; }
        }

        /// <summary>
        /// Gets the tables declared in the documentation.
        /// </summary>
        public IEnumerable<Table> Tables
        {
            get { return this.Declarations != null ? this.Declarations.OfType<Table>() : new Table[0]; }
        }

        /// <summary>
        /// Gets the variables declared in the documentation.
        /// </summary>
        public IEnumerable<Variable> Variables
        {
            get { return this.Declarations != null ? this.Declarations.OfType<Variable>() : new Variable[0]; }
        }

        /// <summary>
        /// Clears the declarations from the documentation.
        /// </summary>
        public void Clear()
        {
            this.Declarations.Clear();
        }

        /// <summary>
        /// Adds a declaration to the documentation.
        /// </summary>
        /// <param name="declaration">The declaration to add.</param>
        public void Add(Declaration declaration)
        {
            if (declaration == null)
                throw new ArgumentNullException("declaration");

            // Merge prototype into table if declaration is a table
            if (declaration is Table)
                MergePrototype(this, (Table)declaration);

            // Add declaration to list of fields
            this.Declarations.Add(declaration);
        }

        /// <summary>
        /// Adds a range of declarations to the documentation.
        /// </summary>
        /// <param name="declarations">The enumerable collection of declarations to add.</param>
        public void AddRange(IEnumerable<Declaration> declarations)
        {
            if (declarations == null)
                throw new ArgumentNullException("declarations");

            foreach (var declaration in declarations)
                this.Add(declaration);
        }

        /// <summary>
        /// Determines whether the documentation already contains a declaration.
        /// </summary>
        /// <param name="declaration">The declaration.</param>
        /// <returns>True, if the declaration already exists; false otherwise.</returns>
        public bool Contains(Declaration declaration)
        {
            if (declaration == null)
                throw new ArgumentNullException("declaration");

            return this.Declarations.Contains(declaration, new DeclarationEqualityComparer<Declaration>());
        }

        /// <summary>
        /// Merges the tables' prototypes into the declared tables.
        /// </summary>
        public void MergePrototypes()
        {
            MergePrototypes(this);
        }

        /// <summary>
        /// Merges the tables' prototypes into the declared tables using a lookup table.
        /// </summary>
        /// <param name="lookupTable"></param>
        public void MergePrototypes(Table lookupTable)
        {
            foreach (var table in this.Tables.Where(t => t.HasPrototype && !t.IsPrototypeMerged))
                MergePrototype(lookupTable, table);
        }

        private static void MergePrototype(Table lookupTable, Table table)
        {
            if (table.HasPrototype)
            {
                var prototypeTable = lookupTable.Resolve<Table>(table.Prototype);
                if (prototypeTable != null)
                    table.Merge(prototypeTable);
            }
        }
    }
}
